package com.app.common.net

import android.util.Log
import com.bumptech.glide.integration.okhttp.BuildConfig
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import okhttp3.*
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import java.io.IOException
import java.util.concurrent.TimeUnit

class HttpProtocol(private val singleTask: Boolean = false, private val timeout: Int = 30) :
    BaseProtocol {

    /**
     *[timeout] sec Unit
     * */
    class TimeOutSet(val timeout: Int)

    class StringConverter : Converter<String> {
        override fun onConvert(content: String?): String {
            return content.orEmpty()
        }
    }

    class TimeOutSetInterceptor : Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            val new = chain.request()
            val tag = new.tag()
            return if (tag is TimeOutSet) {
                chain.withReadTimeout(tag.timeout, TimeUnit.SECONDS).proceed(new)
            } else {
                chain.proceed(new)
            }
        }
    }

    interface Converter<T> {
        fun onConvert(content: String?): T
    }

    private var okHttpClient: OkHttpClient = OkHttpClient.Builder().apply {
        readTimeout(timeout.toLong(), TimeUnit.SECONDS)
        if (BuildConfig.DEBUG) {
            addNetworkInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
                Log.i("HttpProtocol", it)
            }).apply {
                level = HttpLoggingInterceptor.Level.BODY
            })
        }

//        addInterceptor(TimeOutSetInterceptor())
        if (singleTask) {
            dispatcher(Dispatcher().apply { maxRequests = 1 })
        }

    }.build()


    override fun release() {
        okHttpClient.dispatcher().cancelAll()
    }

    fun <T> postAsync(request: Request, converter: Converter<T>): Deferred<T> {
        return okHttpClient.newCall(request).sendAsync(converter)
    }

    fun <T> postAsync(url: String, converter: Converter<T>): Deferred<T> {
        val request = Request.Builder().url(url).post(RequestBody.create(null, "")).build()
        return okHttpClient.newCall(request).sendAsync(converter)
    }

    fun <T> post(url: String, converter: Converter<T>): T {
        val request = Request.Builder().url(url).post(RequestBody.create(null, "")).build()
        val response = okHttpClient.newCall(request).execute()
        return if (response.isSuccessful) {
            converter.onConvert(response.body()?.string())
        } else {
            throw Exception("HTTP " + response.code() + " " + response.message())
        }
    }

    fun <T> getAsync(url: String, query: String? = null, converter: Converter<T>): Deferred<T> {
        var rUrl = url
        if (!query.isNullOrEmpty()) {
            if (!url.endsWith("?")) {
                rUrl += "?"
            }
            rUrl += query
        }
        return okHttpClient.newCall(Request.Builder().url(rUrl).build()).sendAsync(converter)
    }

    fun <T> getAsyncWithTimeout(
        url: String,
        query: String? = null,
        timeout: Int,
        converter: Converter<T>
    ): Deferred<T> {
        var rUrl = url
        if (!query.isNullOrEmpty()) {
            if (!url.endsWith("?")) {
                rUrl += "?"
            }
            rUrl += query
        }
        return okHttpClient.newCall(Request.Builder().url(rUrl).tag(TimeOutSet(timeout)).build())
            .sendAsync(converter)
    }

    fun <T> get(url: String, query: String? = null, converter: Converter<T>): T {
        var rUrl = url
        if (!query.isNullOrEmpty()) {
            if (!url.endsWith("?")) {
                rUrl += "?"
            }
            rUrl += query
        }
        val response = okHttpClient.newCall(Request.Builder().url(rUrl).build()).execute()
        return if (response.isSuccessful) {
            converter.onConvert(response.body()?.string())
        } else {
            throw Exception("HTTP " + response.code() + " " + response.message())
        }
    }

    fun get(url: String): Response {
        return okHttpClient.newCall(Request.Builder().url(url).build()).execute()
    }

    suspend fun getAsync(url: String): Response {
        val call = okHttpClient.newCall(Request.Builder().url(url).build())
        val deferred = CompletableDeferred<Response>()

        deferred.invokeOnCompletion {
            if (deferred.isCancelled) {
                call.cancel()
            }
        }

        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                deferred.completeExceptionally(e)
            }

            override fun onResponse(call: Call, response: Response) {
                if (response.isSuccessful) {
                    deferred.complete(response)
                } else {
                    deferred.completeExceptionally(Exception("HTTP " + response.code() + " " + response.message()))
                }
            }
        })
        return deferred.await()
    }

    private fun <T> Call.sendAsync(converter: Converter<T>): Deferred<T> {
        val deferred = CompletableDeferred<T>()

        deferred.invokeOnCompletion {
            if (deferred.isCancelled) {
                cancel()
            }
        }

        enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                deferred.completeExceptionally(e)
            }

            override fun onResponse(call: Call, response: Response) {
                if (response.isSuccessful) {
                    deferred.complete(converter.onConvert(response.body()?.string()))
                } else {
                    deferred.completeExceptionally(Exception("HTTP " + response.code() + " " + response.message()))
                }
            }
        })

        return deferred
    }

}